﻿using System;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Excel;

namespace BrightSword.OfficeInterop
{
    /// <summary>
    /// Disposable wrapper to properly manage creation of /access to /disposal of Excel workbooks.
    /// </summary>
    /// <example>
    /// The following code opens a specified Excel workbook and makes the Worksheets collection available for use:
    /// 
    /// <code>
    /// using (var app = new ExcelApplicationWrapper(filename))
    /// {
    ///     var worksheets = app.Workbook.Sheets;
    ///
    ///     try
    ///     {
    ///         foreach (var worksheet in sheets)
    ///         {
    ///             try
    ///             {
    ///                 ... // use the worksheet 
    ///             }
    ///             finally
    ///             {
    ///                 Marshal.ReleaseComObject(worksheet);
    ///             }
    ///         }
    ///     }
    ///     finally
    ///     {
    ///         Marshal.ReleaseComObject(worksheets);
    ///     }
    /// }
    ///</code>
    ///     Follow good practice when dealing with COM pointer references, and ensure that the reference is released explicitly every time an assignment is made of a COM object to a local reference
    /// </example>
    /// <remarks>
    /// This code is made available for open-source use at http://excelreader.codeplex.com/
    /// </remarks>
    /// <see cref="http://excelreader.codeplex.com/"/>
    public class ExcelApplicationWrapper : IDisposable
    {
        internal readonly Application Application;

        internal readonly Workbook Workbook;

        internal readonly Workbooks Workbooks;

        /// <summary>
        /// Disposable wrapper to properly manage creation of /access to /disposal of Excel workbooks.
        /// </summary>
        /// <example>
        /// The following code opens a specified Excel workbook and makes the Worksheets collection available for use:
        /// 
        /// <code>
        /// using (var app = new ExcelApplicationWrapper(filename))
        /// {
        ///     var worksheets = app.Workbook.Sheets;
        ///
        ///     try
        ///     {
        ///         foreach (var worksheet in sheets)
        ///         {
        ///             try
        ///             {
        ///                 ... // use the worksheet 
        ///             }
        ///             finally
        ///             {
        ///                 Marshal.ReleaseComObject(worksheet);
        ///             }
        ///         }
        ///     }
        ///     finally
        ///     {
        ///         Marshal.ReleaseComObject(worksheets);
        ///     }
        /// }
        ///</code>
        ///     Follow good practice when dealing with COM pointer references, and ensure that the reference is released explicitly every time an assignment is made of a COM object to a local reference
        /// </example>
        /// <param name="filename">Required parameter specifying the full path to the Excel document to open</param>
        public ExcelApplicationWrapper(string filename)
        {
            Application = new Application();
            Workbooks = Application.Workbooks;
            Workbook = Workbooks.Open(filename);
        }

        #region IDisposable Members

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion

        protected virtual void Dispose(bool disposeManagedAndNative)
        {
            #region Dispose native resources

            do
            {
                if (Workbook == null)
                {
                    continue;
                }

                Workbook.Close(false);
                Marshal.ReleaseComObject(Workbook);
            }
            while (false);

            do
            {
                if (Workbooks == null)
                {
                    continue;
                }

                Workbooks.Close();
                Marshal.ReleaseComObject(Workbooks);
            }
            while (false);

            do
            {
                if (Application == null)
                {
                    return;
                }

                Application.Quit();
                Marshal.ReleaseComObject(Application);
            }
            while (false);

            #endregion
        }
    }
}